/**
 * Created 2008.01.30
 */
package bancosys.tec.rtm.impl.exception.domain;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.persistence.Transient;

import jmine.tec.persist.annotation.Alias;
import jmine.tec.persist.annotation.Comment;
import jmine.tec.persist.annotation.Comments;
import jmine.tec.persist.annotation.Index;
import jmine.tec.utils.Tuple;
import jmine.tec.utils.UnmodifiableTuple;

import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
import org.hibernate.annotations.CollectionOfElements;
import org.hibernate.annotations.MapKey;
import org.hibernate.annotations.Type;

import bancosys.tec.component.Documentation;
import bancosys.tec.persist.authorization.annotation.DisplayName;
import bancosys.tec.persist.bussobj.PersistableBusinessObject;
import bancosys.tec.utils.date.Timestamp;

/**
 * @author Piercio
 */
@Entity
@Table(name = "EXCEPTION_RECORD")
@Alias("EXCREC")
@SequenceGenerator(name = "SEQ_EXCREC", sequenceName = "SEQ_EXCREC")
@Documentation("ARMAZENA OS ERROS (EXCEPTION) OCORRIDOS NO SISTEMA.")
public class ExceptionRecord extends PersistableBusinessObject implements bancosys.tec.rtm.exception.ExceptionRecord {

    public static final int MAX_MSG_LENGTH = 255;

    private long id;

    private Timestamp timestamp;

    private String mensagem;

    private String stack;

    private String user;

    private String system;

    private Map<String, String> context = new HashMap<String, String>();

    /**
     * Contrutor vazio.
     */
    public ExceptionRecord() {
        // This method was intentionally left blank
    }

    /**
     * @return o id do registro.
     */
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_EXCREC")
    @Column(name = "COD_EXCEPTION", nullable = false)
    @Documentation("CODIGO DO ERRO.")
    public long getId() {
        return this.id;
    }

    /**
     * Define o id para a instância.
     * 
     * @param id o id da instância.
     */
    public void setId(long id) {
        this.id = id;
    }

    /**
     * Método para compatibilidade com versões anteriores.
     * 
     * @see getTimestamp().
     * @return a data da exceção.
     */
    @Transient
    public Calendar getData() {
        return this.timestamp.asCalendar();
    }

    /**
     * Define a data da exceção. Método para compatibilidade com versões anteriores.
     * 
     * @see setTimestamp().
     * @param dataParam a data da exceção.
     */
    public void setData(Calendar dataParam) {
        this.timestamp = new Timestamp(dataParam);
    }

    /**
     * @return o timestamp da exceção.
     */
    @Index(suffix = "JT_1")
    @Column(name = "TIMESTAMP_EXCEPTION", nullable = false)
    @Type(type = "bancosys.tec.persist.hibernate.type.TimestampType")
    @Documentation("DATA/HORA DA OCORRENCIA DO ERRO.")
    public Timestamp getTimestamp() {
        return this.timestamp;
    }

    /**
     * Define o timestamp da exceção.
     * 
     * @param timestamp o timestamp da exceção.
     */
    public void setTimestamp(Timestamp timestamp) {
        this.timestamp = timestamp;
    }

    /**
     * @return a mensagem da exceção.
     */
    @Column(name = "MSG_EXCEPTION", length = MAX_MSG_LENGTH)
    @Documentation("MENSAGEM DO ERRO.")
    public String getMensagem() {
        return this.mensagem;
    }

    /**
     * Define a mensagem da exceção. Caso seu tamanho seja maior que MAX_MSG_LENGHT, a mensagem será cortada.
     * 
     * @param mensagem a mensagem da exceção.
     */
    public void setMensagem(String mensagem) {
        if (mensagem != null && mensagem.length() > MAX_MSG_LENGTH) {
            this.mensagem = mensagem.substring(0, MAX_MSG_LENGTH - 1);
        } else {
            this.mensagem = mensagem;
        }
    }

    /**
     * @return o stack trace da exceção.
     */
    @Column(name = "STACK_EXCEPTION", length = 10485760)
    @Type(type = "org.hibernate.type.TextType")
    @Documentation("DETALHE DO ERRO.")
    public String getStack() {
        return this.stack;
    }

    /**
     * Método utilitário que devolve a stackTrace formatada.
     * 
     * @return stackTrace formatada.
     */
    @Transient
    public String getStackFormatada() {
        String result = "";
        if (this.stack != null) {
            result = this.stack.replaceAll("\t", "\n\t").replaceAll("Caused by: ", "\nCaused by: ");
        }
        return result;
    }

    /**
     * Define o stack trace da exceção.
     * 
     * @param stack o stack trace da exceção.
     */
    public void setStack(String stack) {
        this.stack = stack;
    }

    /**
     * @return the context
     */
    @Index(suffix = "JT_0", columns = "COD_EXCEPTION")
    @CollectionOfElements
    @Cascade({ CascadeType.ALL, CascadeType.DELETE_ORPHAN })
    @JoinTable(name = "EXC_EXTRA_INFO", joinColumns = @JoinColumn(name = "COD_EXCEPTION"))
    @DisplayName("Mapa de informações adicionais dos erros.")
    @MapKey(columns = { @Column(name = "NAME", nullable = false) })
    @Column(name = "VALUE", nullable = false)
    @Alias("EXCCTX")
    @Comments({ @Comment(table = "EXC_EXTRA_INFO", value = "INFORMAÇÕES ADICIONAIS SOBRE OS ERROS."),
            @Comment(table = "EXC_EXTRA_INFO", column = "COD_EXCEPTION", value = "CODIGO DO ERRO."),
            @Comment(table = "EXC_EXTRA_INFO", column = "NAME", value = "NOME DA PROPRIEDADE DO CONTEXTO DA EXCECAO"),
            @Comment(table = "EXC_EXTRA_INFO", column = "VALUE", value = "VALOR DA PROPRIEDADE NOMEADA") })
    public Map<String, String> getContext() {
        return this.context;
    }

    /**
     * @return {@link List}
     */
    @Transient
    public List<Tuple<String, String>> getContextAsList() {
        List<Tuple<String, String>> list = new ArrayList<Tuple<String, String>>(this.getContext().size());
        for (Entry<String, String> entry : this.getContext().entrySet()) {
            list.add(UnmodifiableTuple.newTuple(entry.getKey(), entry.getValue()));
        }
        return list;
    }

    /**
     * @param context the context to set
     */
    public void setContext(Map<String, String> context) {
        this.context = context;
    }

    /**
     * @return the user
     */
    @Column(name = "USER_EXCEPTION")
    @Documentation("USUÁRIO LOGADO NO MOMENTO EM QUE O ERRO OCORREU.")
    public String getUser() {
        return this.user;
    }

    /**
     * @param user the user to set
     */
    public void setUser(String user) {
        this.user = user;
    }

    /**
     * @return the system
     */
    @Column(name = "SYSTEM_EXCEPTION")
    @Documentation("SISTEMA EM QUE O ERRO OCORREU.")
    public String getSystem() {
        return this.system;
    }

    /**
     * @param system the system to set
     */
    public void setSystem(String system) {
        this.system = system;
    }
}